package com.ggb.reactive.controller.admin

import com.ggb.reactive.constant.ACTION_FAIL
import com.ggb.reactive.constant.ACTION_SUCCESS
import com.ggb.reactive.constant.USER_ID
import com.ggb.reactive.domain.dto.SetmealDTO
import com.ggb.reactive.domain.dto.SetmealPageQueryDTO
import com.ggb.reactive.domain.entity.Setmeal
import com.ggb.reactive.domain.entity.SetmealDish
import com.ggb.reactive.domain.vo.SetmealVO
import com.ggb.reactive.extension.log
import com.ggb.reactive.model.PageResult
import com.ggb.reactive.repository.SetmealDishRepository
import com.ggb.reactive.repository.SetmealRepository
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import lombok.RequiredArgsConstructor
import org.springframework.beans.BeanUtils
import reactor.core.publisher.Mono
import com.ggb.reactive.model.Result
import com.ggb.reactive.service.SetmealService
import org.springframework.data.redis.core.ReactiveRedisTemplate
import org.springframework.data.redis.core.ScanOptions
import org.springframework.transaction.annotation.Transactional
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import org.springframework.web.server.ServerWebExchange
import reactor.core.scheduler.Schedulers
import java.time.Instant


@RestController
@RequestMapping("/admin/setmeal")
@RequiredArgsConstructor
@Tag(name = "套餐相关")
class SetmealController(
    private val setmealRepository: SetmealRepository,
    private val setmealDishRepository: SetmealDishRepository,
    private val setmealService: SetmealService,
    private val reactiveRedisTemplate: ReactiveRedisTemplate<String, Any>
) {
    @PostMapping
    @Operation(summary = "新增套餐")
    @Transactional
    fun add(@RequestBody @Validated setmealDTO: SetmealDTO,exchange: ServerWebExchange): Mono<Result<String>> {
        val setmeal = Setmeal()
        val userId = (exchange.attributes[USER_ID] as Int).toLong()
        BeanUtils.copyProperties(setmealDTO, setmeal)
        setmeal.updateUser = userId
        setmeal.updateTime = Instant.now()

        return setmealRepository.save(setmeal).flatMap {
            val setmealDishEntities = setmealDTO.setmealDishes.map { setmealDishDTO ->
                val setmealDish = SetmealDish()
                BeanUtils.copyProperties(setmealDishDTO, setmealDish)
                setmealDish.setmealId = it.id ?: throw IllegalArgumentException("id is null")
                setmealDish
            }
            setmealDishRepository.saveAll(setmealDishEntities).then(Mono.just(Result.success(ACTION_SUCCESS)))
        }
            .publishOn(Schedulers.boundedElastic())
            .doOnSuccess {
                val key = "setmealCache_${setmealDTO.categoryId}"
                reactiveRedisTemplate.opsForValue().delete(key).subscribe() // 清除缓存
            }
            .onErrorResume { error ->
            log.error("Error occurred during setmeal dishes save", error)
            Mono.just(Result.error(ACTION_FAIL))
        }.switchIfEmpty(Mono.just(Result.error(ACTION_FAIL)))
    }

    @PutMapping
    @Operation(summary = "编辑套餐")
    @Transactional
    fun update(@RequestBody @Validated setmealDTO: SetmealDTO, exchange: ServerWebExchange): Mono<Result<String>> {
        val userId = (exchange.attributes[USER_ID] as Int).toLong()
        val setmeal = Setmeal()
        BeanUtils.copyProperties(setmealDTO, setmeal)
        setmeal.updateUser = userId
        setmeal.updateTime = Instant.now()
        return setmealService.updateSetmeal(setmeal)
            .flatMap {
                setmealDishRepository.deleteBySetmealId(setmeal.id!!)
            }
            .flatMap {
                val setmealDish = setmealDTO.setmealDishes.map {
                    it.setmealId = setmeal.id
                    it
                }
                setmealDishRepository.saveAll(setmealDish).then(Mono.just(Result.success(ACTION_SUCCESS)))
            }
            .publishOn(Schedulers.boundedElastic())
            .doOnSuccess {
                val key = "setmealCache_${setmealDTO.categoryId}"
                reactiveRedisTemplate.opsForValue().delete(key).subscribe() // 清除缓存
            }.onErrorResume {
                log.info("错误信息 {}", it.message)
                Mono.just(Result.error(ACTION_FAIL))
            }
            .map {
                Result.success(ACTION_SUCCESS)
            }.switchIfEmpty(Mono.just(Result.error(ACTION_FAIL)))
    }

    @GetMapping("/page")
    @Operation(summary = "套餐列表分页查询")
    fun page(setmealPageQueryDTO: SetmealPageQueryDTO): Mono<Result<PageResult<Setmeal>>> {
        return setmealService.findSetmealsByCriteria(setmealPageQueryDTO)
    }

    @GetMapping("/{id}")
    @Operation(summary = "获取套餐详情")
    fun detail(@PathVariable id: Long): Mono<Result<SetmealVO>> {
        return setmealRepository.findById(id)
            .flatMap { setmeal ->
                setmealDishRepository.findSetmealDishBySetmealId(id)
                    .collectList()
                    .map {
                        val setmealVO = SetmealVO()
                        BeanUtils.copyProperties(setmeal, setmealVO)
                        setmealVO.setmealDishes = it
                        Result.success(setmealVO)
                    }
            }
            .switchIfEmpty(Mono.just(Result.error("未查询到套餐")))
    }

    /**
     * 启用禁用菜品
     */
    @Operation(summary = "启用禁用套餐")
    @PostMapping("/status/{status}")
    fun startOrStop(@PathVariable status: Int?, id: Long?, exchange: ServerWebExchange): Mono<Result<String>> {
        if (id == null) return Mono.just(Result.error("id名不能为空"))
        if (status == null) return Mono.just(Result.error("status不能为空"))
        val userId = (exchange.attributes[USER_ID] as Int).toLong()

        return setmealRepository.updateStatusById(id, status, Instant.now(), userId)
            .map {
                Result.success(ACTION_SUCCESS)
            }
            .publishOn(Schedulers.boundedElastic())
            .doOnSuccess {
                cleanCache("setmealCache_*").subscribe()
            }
            .onErrorResume {

                Mono.just(Result.error(ACTION_FAIL))
            }
    }

    @DeleteMapping
    @Operation(summary = "根据id删除套餐")
    @Transactional
    fun delete(@RequestParam ids: List<Long>): Mono<Result<String>> {
        return setmealRepository.deleteAllById(ids)
            .then(Mono.defer { setmealDishRepository.deleteBySetmealIdIn(ids) })
            .map {
                Result.success(ACTION_SUCCESS)
            }
            .publishOn(Schedulers.boundedElastic())
            .doOnSuccess {
                cleanCache("setmealCache_*").subscribe()
            }
            .onErrorResume {
                Mono.just(Result.error(ACTION_FAIL))
            }
    }

    /**
     * 清除缓存
     */
    fun cleanCache(pattern: String): Mono<Void> {
        val scanOptions = ScanOptions.scanOptions().match(pattern).build()
        return reactiveRedisTemplate.scan(scanOptions) // 正确使用 scan 方法
            .flatMap { key ->
                reactiveRedisTemplate.delete(key) // 删除找到的每个键
            }
            .then() // 在所有删除操作完成后返回一个 Mono<Void>
    }

}

